/*
* Copyright (C) Microsoft. All rights reserved.  
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not 
* use this file except in compliance with the License.  You may obtain a copy 
* of the License at http://www.apache.org/licenses/LICENSE-2.0  
* 
* THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED 
* WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, 
* MERCHANTABLITY OR NON-INFRINGEMENT. 
*
* See the Apache License, Version 2.0 for specific language governing 
* permissions and limitations under the License.
*
*/
#include "pch.h"
#include <baja/transition_tracking/tracked_object.h>
#include <baja/transition_tracking/transition_tracking_helpers.h>

namespace baja { namespace transition_tracking {

namespace b = baja;
namespace bm = baja::model;
namespace bt = baja::transition_tracking;

// TRACK: bug 479 : need to use console tracing.
#define DEBUG_CONSOLE_TRACE(x)
#define DEBUG_CONSOLE_TRACE_F(X)

#ifndef DEBUG_CONSOLE_TRACE
#define DEBUG_CONSOLE_TRACE(x) OutputDebugString(x)
#define DEBUG_CONSOLE_TRACE_F(x) OutputDebugString((x).str().c_str())
#endif

tracked_object::tracked_object() :
    _undoRedoInProgress(false)
{
}

void tracked_object::setGenericParent(const std::shared_ptr<igeneric_model>& parent)
{
    generic_model::setGenericParent(parent);

    buildTransitionPath(_objectPath);
}

void tracked_object::setTrackingService(
    const std::shared_ptr<itransition_tracking_service>& trackingService)
{
    _trackingService = trackingService;
}

void tracked_object::registerInternal(
    const b::uint32& transitionType,
    const b::uint32& objectType,
    const b::uint32& objectId,
    const b::uint32& objectOrder)
{
    if (_undoRedoInProgress == true)
    {
        return;
    }

    // register in the undo-redo service
    // (1) pack the necessary information...
    std::shared_ptr<bt::generic_transition_parameters> info = std::make_shared<bt::generic_transition_parameters>(
        transitionType,
        objectType,
        objectId);
    info->_objectOrder = objectOrder;

    // (2) add the transition to the bundle...
    _trackingService->addTransition(_objectPath, info);
}

void tracked_object::registerAttachChild(
    const b::uint32& transitionType,
    const b::uint32& childType,
    const b::uint32& childId,
    const b::uint32& childOrder)
{
    DEBUG_CONSOLE_TRACE(L"tracked_object::registerAttachChild()\n");

    registerInternal(
        transitionType,
        childType,
        childId,
        childOrder);
}

void tracked_object::registerCreateChild(
    const b::uint32& transitionType,
    const b::uint32& childType,
    const b::uint32& childId)
{
    DEBUG_CONSOLE_TRACE(L"tracked_object::registerCreateChild()\n");

    registerInternal(
        transitionType,
        childType,
        childId,
        0);
}

void tracked_object::registerDetachChild(
    const b::uint32& transitionType,
    const b::uint32& childType,
    const b::uint32& childId,
    const b::uint32& childOrder)
{
    DEBUG_CONSOLE_TRACE(L"tracked_object::registerDetachChild()\n");

    registerInternal(
        transitionType,
        childType,
        childId,
        childOrder);
}

void tracked_object::registerDeleteChild(
    const b::uint32& transitionType,
    const b::uint32& childType,
    const b::uint32& childId)
{
    DEBUG_CONSOLE_TRACE(L"tracked_object::registerDeleteChild()\n");

    registerInternal(
        transitionType,
        childType,
        childId,
        0);
}

void tracked_object::registerCustomEvent(
    const b::uint32& transitionType
    )
{
    DEBUG_CONSOLE_TRACE(L"tracked_object::registerCustomEvent()\n");

    registerInternal(
        transitionType,
        0,                                          // object type
        _objectPath[ _objectPath.size() - 1].id,      // object id
        0);                                         // object order
}

void tracked_object::buildTransitionPath(
    std::vector<bt::transition_path_node>& path
    )
{
    DEBUG_CONSOLE_TRACE_F(
        boost::wformat(L"tracked_object::buildTransitionPath() : model id = %i - type = %i - path count = %i\n") %
        genericIdentity() %
        genericTypeToken() %
        path.size()
        );

    // add parents
    std::shared_ptr<bt::itransition_target> parent = std::dynamic_pointer_cast<bt::itransition_target>(genericParent());
    if (parent != nullptr)
    {
        parent->buildTransitionPath(path);
    }

    // add self
    bt::transition_path_node node;
    node.id = genericIdentity();
    node.typeToken = genericTypeToken();
    path.push_back(node);
}

const std::vector<bt::transition_path_node>& tracked_object::transitionPath()
{
    return _objectPath;
}

void tracked_object::undo(
    const std::vector<bt::transition_path_node>& path,
    const b::uint32& index,
    const std::shared_ptr<bt::itransition_parameters>& transitionParameters
    )
{
    DEBUG_CONSOLE_TRACE_F(
        boost::wformat(L"tracked_object::undo() : model id = %i - path count = %i - path index = %i\n") %
        genericIdentity() %
        path.size() %
        index
        );

    if ((path[index].typeToken != genericTypeToken()) || (path[index].id != genericIdentity()))
    {
        BAJA_ASSERT(false, L"incorrect routing!");
        return;
    }
    if (index == path.size() - 1)
    {
        undoSelfAction(transitionParameters);
    }
    else
    {
        undoChildAction(path, index + 1, transitionParameters);
    }


}

void tracked_object::redo(
    const std::vector<bt::transition_path_node>& path,
    const b::uint32& index,
    const std::shared_ptr<bt::itransition_parameters>& transitionParameters
    )
{
    DEBUG_CONSOLE_TRACE_F(
        boost::wformat(L"tracked_object::redo() : model id = %i - path count = %i - path index = %i\n") %
        genericIdentity() %
        path.size() %
        index
        );

    if ((path[index].typeToken != genericTypeToken()) || (path[index].id != genericIdentity()))
    {
        BAJA_ASSERT(false, L"incorrect routing!");
        return;
    }
    if (index == path.size() - 1)
    {
        redoSelfAction(transitionParameters);
    }
    else
    {
        redoChildAction(path, index + 1, transitionParameters);
    }
}

void tracked_object::undoSelfAction(
    const std::shared_ptr<bt::itransition_parameters>& transitionParameters
    )
{
}

void tracked_object::undoChildAction(
    const std::vector<bt::transition_path_node>& path,
    const b::uint32& childIndex,
    const std::shared_ptr<bt::itransition_parameters>& transitionParameters
    )
{
}

void tracked_object::redoSelfAction(
    const std::shared_ptr<bt::itransition_parameters>& transitionParameters
    )
{
}

void tracked_object::redoChildAction(
    const std::vector<bt::transition_path_node>& path,
    const b::uint32& childIndex,
    const std::shared_ptr<bt::itransition_parameters>& transitionParameters)
{
}

}} // baja::transition_tracking